/* src/interfaces/ecpg/ecpglib/connect.c */

#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"

#include "ecpg-pthread-win32.h"
#include "ecpgerrno.h"
#include "ecpglib.h"
#include "ecpglib_extern.h"
#include "ecpgtype.h"
#include "sqlca.h"

#ifdef HAVE_USELOCALE
locale_t	ecpg_clocale = (locale_t) 0;
#endif

#ifdef ENABLE_THREAD_SAFETY
static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_key_t actual_connection_key;
static pthread_once_t actual_connection_key_once = PTHREAD_ONCE_INIT;
#endif
static struct connection *actual_connection = NULL;
static struct connection *all_connections = NULL;

#ifdef ENABLE_THREAD_SAFETY
static void fc_ecpg_actual_connection_init(void)
{
	pthread_key_create(&actual_connection_key, NULL);
}

void ecpg_pthreads_init(void)
{
	pthread_once(&actual_connection_key_once, fc_ecpg_actual_connection_init);
}
#endif

static struct connection *
fc_ecpg_get_connection_nr(const char *fc_connection_name)
{
	struct connection *fc_ret = NULL;

	if ((fc_connection_name == NULL) || (strcmp(fc_connection_name, "CURRENT") == 0))
	{
#ifdef ENABLE_THREAD_SAFETY
		ecpg_pthreads_init();	/* 确保 actual_connection_key 是有效的 */

		fc_ret = pthread_getspecific(actual_connection_key);

		/*
		 * 如果此线程的 TSD 中没有连接，获取全局默认
		 * 连接，并希望用户知道他们在做什么（即使用
		 * 自己的互斥锁来保护该连接免受并发访问）
		 */
		if (fc_ret == NULL)
			/* 没有 TSD 连接，使用全局 */
			fc_ret = actual_connection;
#else
		ret = actual_connection;
#endif
	}
	else
	{
		struct connection *fc_con;

		for (fc_con = all_connections; fc_con != NULL; fc_con = fc_con->next)
		{
			if (strcmp(fc_connection_name, fc_con->name) == 0)
				break;
		}
		fc_ret = fc_con;
	}

	return fc_ret;
}

struct connection *
ecpg_get_connection(const char *fc_connection_name)
{
	struct connection *fc_ret = NULL;

	if ((fc_connection_name == NULL) || (strcmp(fc_connection_name, "CURRENT") == 0))
	{
#ifdef ENABLE_THREAD_SAFETY
		ecpg_pthreads_init();	/* 确保 actual_connection_key 是有效的 */

		fc_ret = pthread_getspecific(actual_connection_key);

		/*
		 * 如果此线程的 TSD 中没有连接，获取全局默认
		 * 连接，并希望用户知道他们在做什么（即使用
		 * 自己的互斥锁来保护该连接免受并发访问）
		 */
		if (fc_ret == NULL)
			/* 这里也没有 TSD 连接，使用全局 */
			fc_ret = actual_connection;
#else
		ret = actual_connection;
#endif
	}
	else
	{
#ifdef ENABLE_THREAD_SAFETY
		pthread_mutex_lock(&connections_mutex);
#endif

		fc_ret = fc_ecpg_get_connection_nr(fc_connection_name);

#ifdef ENABLE_THREAD_SAFETY
		pthread_mutex_unlock(&connections_mutex);
#endif
	}

	return fc_ret;
}

static void fc_ecpg_finish(struct connection *fc_act)
{
	if (fc_act != NULL)
	{
		struct ECPGtype_information_cache *fc_cache,
				   *fc_ptr;

		ecpg_deallocate_all_conn(0, ECPG_COMPAT_PGSQL, fc_act);
		PQfinish(fc_act->connection);

		/*
		 * 不需要锁定 connections_mutex - 我们总是通过
		 * ECPGdisconnect 或 ECPGconnect 被调用，它们持有锁
		 */

		/* 从列表中移除 act */
		if (fc_act == all_connections)
			all_connections = fc_act->next;
		else
		{
			struct connection *fc_con;

			for (fc_con = all_connections; fc_con->next && fc_con->next != fc_act; fc_con = fc_con->next);
			if (fc_con->next)
				fc_con->next = fc_act->next;
		}

#ifdef ENABLE_THREAD_SAFETY
		if (pthread_getspecific(actual_connection_key) == fc_act)
			pthread_setspecific(actual_connection_key, all_connections);
#endif
		if (actual_connection == fc_act)
			actual_connection = all_connections;

		ecpg_log("ecpg_finish: connection %s closed\n", fc_act->name ? fc_act->name : "(null)");

		for (fc_cache = fc_act->cache_head; fc_cache; fc_ptr = fc_cache, fc_cache = fc_cache->next, ecpg_free(fc_ptr));
		ecpg_free(fc_act->name);
		ecpg_free(fc_act);
		/* 当最后一个连接关闭时删除游标变量 */
		if (all_connections == NULL)
		{
			struct var_list *fc_iv_ptr;

			for (; ivlist; fc_iv_ptr = ivlist, ivlist = ivlist->next, ecpg_free(fc_iv_ptr));
		}
	}
	else
		ecpg_log("ecpg_finish: called an extra time\n");
}

bool ECPGsetcommit(int fc_lineno, const char *fc_mode, const char *fc_connection_name)
{
	struct connection *fc_con = ecpg_get_connection(fc_connection_name);
	PGresult   *fc_results;

	if (!ecpg_init(fc_con, fc_connection_name, fc_lineno))
		return false;

	ecpg_log("ECPGsetcommit on line %d: action \"%s\"; connection \"%s\"\n", fc_lineno, fc_mode, fc_con->name);

	if (fc_con->autocommit && strncmp(fc_mode, "off", strlen("off")) == 0)
	{
		if (PQtransactionStatus(fc_con->connection) == PQTRANS_IDLE)
		{
			fc_results = PQexec(fc_con->connection, "begin transaction");
			if (!ecpg_check_PQresult(fc_results, fc_lineno, fc_con->connection, ECPG_COMPAT_PGSQL))
				return false;
			PQclear(fc_results);
		}
		fc_con->autocommit = false;
	}
	else if (!fc_con->autocommit && strncmp(fc_mode, "on", strlen("on")) == 0)
	{
		if (PQtransactionStatus(fc_con->connection) != PQTRANS_IDLE)
		{
			fc_results = PQexec(fc_con->connection, "commit");
			if (!ecpg_check_PQresult(fc_results, fc_lineno, fc_con->connection, ECPG_COMPAT_PGSQL))
				return false;
			PQclear(fc_results);
		}
		fc_con->autocommit = true;
	}

	return true;
}

bool ECPGsetconn(int fc_lineno, const char *fc_connection_name)
{
	struct connection *fc_con = ecpg_get_connection(fc_connection_name);

	if (!ecpg_init(fc_con, fc_connection_name, fc_lineno))
		return false;

#ifdef ENABLE_THREAD_SAFETY
	pthread_setspecific(actual_connection_key, fc_con);
#else
	actual_connection = con;
#endif
	return true;
}


static void fc_ECPGnoticeReceiver(void *fc_arg, const PGresult *fc_result)
{
	char	   *fc_sqlstate = PQresultErrorField(fc_result, PG_DIAG_SQLSTATE);
	char	   *fc_message = PQresultErrorField(fc_result, PG_DIAG_MESSAGE_PRIMARY);
	struct sqlca_t *fc_sqlca = ECPGget_sqlca();
	int			fc_sqlcode;

	if (fc_sqlca == NULL)
	{
		ecpg_log("out of memory");
		return;
	}

	(void) fc_arg;					/* 让编译器安静 */
	if (fc_sqlstate == NULL)
		fc_sqlstate = ECPG_SQLSTATE_ECPG_INTERNAL_ERROR;

	if (fc_message == NULL)		/* 不应该发生，但需要确保 */
		fc_message = ecpg_gettext("empty message text");

	/* 这些不是警告 */
	if (strncmp(fc_sqlstate, "00", 2) == 0)
		return;

	ecpg_log("ECPGnoticeReceiver: %s\n", fc_message);

	/* 为了向后兼容而映射到 SQLCODE */
	if (strcmp(fc_sqlstate, ECPG_SQLSTATE_INVALID_CURSOR_NAME) == 0)
		fc_sqlcode = ECPG_WARNING_UNKNOWN_PORTAL;
	else if (strcmp(fc_sqlstate, ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION) == 0)
		fc_sqlcode = ECPG_WARNING_IN_TRANSACTION;
	else if (strcmp(fc_sqlstate, ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION) == 0)
		fc_sqlcode = ECPG_WARNING_NO_TRANSACTION;
	else if (strcmp(fc_sqlstate, ECPG_SQLSTATE_DUPLICATE_CURSOR) == 0)
		fc_sqlcode = ECPG_WARNING_PORTAL_EXISTS;
	else
		fc_sqlcode = 0;

	strncpy(fc_sqlca->sqlstate, fc_sqlstate, sizeof(fc_sqlca->sqlstate));
	fc_sqlca->sqlcode = fc_sqlcode;
	fc_sqlca->sqlwarn[2] = 'W';
	fc_sqlca->sqlwarn[0] = 'W';

	strncpy(fc_sqlca->sqlerrm.sqlerrmc, fc_message, sizeof(fc_sqlca->sqlerrm.sqlerrmc));
	fc_sqlca->sqlerrm.sqlerrmc[sizeof(fc_sqlca->sqlerrm.sqlerrmc) - 1] = 0;
	fc_sqlca->sqlerrm.sqlerrml = strlen(fc_sqlca->sqlerrm.sqlerrmc);

	ecpg_log("raising sqlcode %d\n", fc_sqlcode);
}

/* 这包含一些快速修补，需要清理，但它有效 */
bool ECPGconnect(int fc_lineno, int fc_c, const char *fc_name, const char *fc_user, const char *fc_passwd, const char *fc_connection_name, int fc_autocommit)
{
	struct sqlca_t *fc_sqlca = ECPGget_sqlca();
	enum COMPAT_MODE fc_compat = fc_c;
	struct connection *fc_this;
	int			fc_i,
				fc_connect_params = 0;
	char	   *fc_dbname = fc_name ? ecpg_strdup(fc_name, fc_lineno) : NULL,
			   *fc_host = NULL,
			   *fc_tmp,
			   *fc_port = NULL,
			   *fc_realname = NULL,
			   *fc_options = NULL;
	const char **fc_conn_keywords;
	const char **fc_conn_values;

	if (fc_sqlca == NULL)
	{
		ecpg_raise(fc_lineno, ECPG_OUT_OF_MEMORY,
				   ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
		ecpg_free(fc_dbname);
		return false;
	}

	ecpg_init_sqlca(fc_sqlca);

	/*
	 * 清除 auto_mem 结构，因为某些错误处理函数可能
	 * 访问它
	 */
	ecpg_clear_auto_mem();

	if (INFORMIX_MODE(fc_compat))
	{
		char	   *fc_envname;

		/*
		 * Informix 使用环境变量 DBPATH 来覆盖
		 * 此处给出的连接参数。我们用 PG_DBPATH 进行同样的操作，因为
		 * 语法不同。
		 */
		fc_envname = getenv("PG_DBPATH");
		if (fc_envname)
		{
			ecpg_free(fc_dbname);
			fc_dbname = ecpg_strdup(fc_envname, fc_lineno);
		}
	}

	if (fc_dbname == NULL && fc_connection_name == NULL)
		fc_connection_name = "DEFAULT";

#if ENABLE_THREAD_SAFETY
	ecpg_pthreads_init();
#endif

	/* 检查标识符是否唯一 */
	if (ecpg_get_connection(fc_connection_name))
	{
		ecpg_free(fc_dbname);
		ecpg_log("ECPGconnect: connection identifier %s is already in use\n",
				 fc_connection_name);
		return false;
	}

	if ((fc_this = (struct connection *) ecpg_alloc(sizeof(struct connection), fc_lineno)) == NULL)
	{
		ecpg_free(fc_dbname);
		return false;
	}

	if (fc_dbname != NULL)
	{
		/* 从 dbname 获取详细信息 */
		if (strncmp(fc_dbname, "tcp:", 4) == 0 || strncmp(fc_dbname, "unix:", 5) == 0)
		{
			int			fc_offset = 0;

			/*
			 * 仅允许 tcp 和 unix 协议
			 */
			if (strncmp(fc_dbname, "tcp:", 4) == 0)
				fc_offset = 4;
			else if (strncmp(fc_dbname, "unix:", 5) == 0)
				fc_offset = 5;

			if (strncmp(fc_dbname + fc_offset, "postgresql://", strlen("postgresql://")) == 0)
			{

				/*------
				 * 新样式：
				 *	<tcp|unix>:postgresql://server[:port][/db-name][?options]
				 *------
				 */
				fc_offset += strlen("postgresql://");

				fc_tmp = strrchr(fc_dbname + fc_offset, '?');
				if (fc_tmp != NULL)	/* 给定选项 */
				{
					fc_options = ecpg_strdup(fc_tmp + 1, fc_lineno);
					*fc_tmp = '\0';
				}

				fc_tmp = last_dir_separator(fc_dbname + fc_offset);
				if (fc_tmp != NULL)	/* 给定数据库名称 */
				{
					if (fc_tmp[1] != '\0') /* 非空数据库名称 */
					{
						fc_realname = ecpg_strdup(fc_tmp + 1, fc_lineno);
						fc_connect_params++;
					}
					*fc_tmp = '\0';
				}

				fc_tmp = strrchr(fc_dbname + fc_offset, ':');
				if (fc_tmp != NULL)	/* 给定端口号 */
				{
					*fc_tmp = '\0';
					fc_port = ecpg_strdup(fc_tmp + 1, fc_lineno);
					fc_connect_params++;
				}

				if (strncmp(fc_dbname, "unix:", 5) == 0)
				{
					/*
					 * 此处使用 "127.0.0.1" 的替代方案已被弃用
					 * 并且没有文档；为了向后兼容，我们将保留它，
					 * 但不扩展以允许 IPv6。
					 */
					if (strcmp(fc_dbname + fc_offset, "localhost") != 0 &&
						strcmp(fc_dbname + fc_offset, "127.0.0.1") != 0)
					{
						ecpg_log("ECPGconnect: non-localhost access via sockets on line %d\n", fc_lineno);
						ecpg_raise(fc_lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, fc_realname ? fc_realname : ecpg_gettext("<DEFAULT>"));
						if (fc_host)
							ecpg_free(fc_host);
						if (fc_port)
							ecpg_free(fc_port);
						if (fc_options)
							ecpg_free(fc_options);
						if (fc_realname)
							ecpg_free(fc_realname);
						if (fc_dbname)
							ecpg_free(fc_dbname);
						free(fc_this);
						return false;
					}
				}
				else
				{
					if (*(fc_dbname + fc_offset) != '\0')
					{
						fc_host = ecpg_strdup(fc_dbname + fc_offset, fc_lineno);
						fc_connect_params++;
					}
				}
			}
		}
		else
		{
			/* 旧样式： dbname[@server][:port] */
			fc_tmp = strrchr(fc_dbname, ':');
			if (fc_tmp != NULL)	/* 给定端口号 */
			{
				fc_port = ecpg_strdup(fc_tmp + 1, fc_lineno);
				fc_connect_params++;
				*fc_tmp = '\0';
			}

			fc_tmp = strrchr(fc_dbname, '@');
			if (fc_tmp != NULL)	/* 给定主机名 */
			{
				fc_host = ecpg_strdup(fc_tmp + 1, fc_lineno);
				fc_connect_params++;
				*fc_tmp = '\0';
			}

			if (strlen(fc_dbname) > 0)
			{
				fc_realname = ecpg_strdup(fc_dbname, fc_lineno);
				fc_connect_params++;
			}
			else
				fc_realname = NULL;
		}
	}
	else
		fc_realname = NULL;

	/*
	 * 计算下面分配的选项数量（这可能导致过估计，
	 * 这没关系）。
	 */
	if (fc_options)
		for (fc_i = 0; fc_options[fc_i]; fc_i++)
			if (fc_options[fc_i] == '=')
				fc_connect_params++;

	if (fc_user && strlen(fc_user) > 0)
		fc_connect_params++;
	if (fc_passwd && strlen(fc_passwd) > 0)
		fc_connect_params++;

	/*
	 * 为所有连接参数分配足够的空间。 在操作连接列表之前
	 * 进行这些分配，以便在失败时简化错误处理。
	 */
	fc_conn_keywords = (const char **) ecpg_alloc((fc_connect_params + 1) * sizeof(char *), fc_lineno);
	fc_conn_values = (const char **) ecpg_alloc(fc_connect_params * sizeof(char *), fc_lineno);
	if (fc_conn_keywords == NULL || fc_conn_values == NULL)
	{
		if (fc_host)
			ecpg_free(fc_host);
		if (fc_port)
			ecpg_free(fc_port);
		if (fc_options)
			ecpg_free(fc_options);
		if (fc_realname)
			ecpg_free(fc_realname);
		if (fc_dbname)
			ecpg_free(fc_dbname);
		if (fc_conn_keywords)
			ecpg_free(fc_conn_keywords);
		if (fc_conn_values)
			ecpg_free(fc_conn_values);
		free(fc_this);
		return false;
	}

	/* 将连接添加到我们的列表中 */
#ifdef ENABLE_THREAD_SAFETY
	pthread_mutex_lock(&connections_mutex);
#endif

	/*
	 * ... 但首先，确保我们创建了 ecpg_clocale。依赖于
	 * 保持 connections_mutex，以确保这仅由一个线程完成。
	 */
#ifdef HAVE_USELOCALE
	if (!ecpg_clocale)
	{
		ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
		if (!ecpg_clocale)
		{
#ifdef ENABLE_THREAD_SAFETY
			pthread_mutex_unlock(&connections_mutex);
#endif
			ecpg_raise(fc_lineno, ECPG_OUT_OF_MEMORY,
					   ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
			if (fc_host)
				ecpg_free(fc_host);
			if (fc_port)
				ecpg_free(fc_port);
			if (fc_options)
				ecpg_free(fc_options);
			if (fc_realname)
				ecpg_free(fc_realname);
			if (fc_dbname)
				ecpg_free(fc_dbname);
			if (fc_conn_keywords)
				ecpg_free(fc_conn_keywords);
			if (fc_conn_values)
				ecpg_free(fc_conn_values);
			free(fc_this);
			return false;
		}
	}
#endif

	if (fc_connection_name != NULL)
		fc_this->name = ecpg_strdup(fc_connection_name, fc_lineno);
	else
		fc_this->name = ecpg_strdup(fc_realname, fc_lineno);

	fc_this->cache_head = NULL;
	fc_this->prep_stmts = NULL;

	if (all_connections == NULL)
		fc_this->next = NULL;
	else
		fc_this->next = all_connections;

	all_connections = fc_this;
#ifdef ENABLE_THREAD_SAFETY
	pthread_setspecific(actual_connection_key, all_connections);
#endif
	actual_connection = all_connections;

	ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
			 fc_realname ? fc_realname : "<DEFAULT>",
			 fc_host ? fc_host : "<DEFAULT>",
			 fc_port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : fc_port) : "<DEFAULT>",
			 fc_options ? "with options " : "", fc_options ? fc_options : "",
			 (fc_user && strlen(fc_user) > 0) ? "for user " : "", fc_user ? fc_user : "");

	fc_i = 0;
	if (fc_realname)
	{
		fc_conn_keywords[fc_i] = "dbname";
		fc_conn_values[fc_i] = fc_realname;
		fc_i++;
	}
	if (fc_host)
	{
		fc_conn_keywords[fc_i] = "host";
		fc_conn_values[fc_i] = fc_host;
		fc_i++;
	}
	if (fc_port)
	{
		fc_conn_keywords[fc_i] = "port";
		fc_conn_values[fc_i] = fc_port;
		fc_i++;
	}
	if (fc_user && strlen(fc_user) > 0)
	{
		fc_conn_keywords[fc_i] = "user";
		fc_conn_values[fc_i] = fc_user;
		fc_i++;
	}
	if (fc_passwd && strlen(fc_passwd) > 0)
	{
		fc_conn_keywords[fc_i] = "password";
		fc_conn_values[fc_i] = fc_passwd;
		fc_i++;
	}
	if (fc_options)
	{
		char	   *fc_str;

		/*
		 * 选项字符串包含以 '&' 分隔的 "关键字=值" 对。我们
		 * 必须将其拆分成关键字和值以传递给 libpq（在选项
		 * 字符串上乱写是可以的）。我们忽略每个关键字或值
		 * 前面的空格。
		 */
		for (fc_str = fc_options; *fc_str;)
		{
			int			fc_e,
						fc_a;
			char	   *fc_token1,
					   *fc_token2;

			/* 跳过关键字前的空格 */
			for (fc_token1 = fc_str; *fc_token1 == ' '; fc_token1++)
				 /* 跳过 */ ;
			/* 找到关键字的结束 */
			for (fc_e = 0; fc_token1[fc_e] && fc_token1[fc_e] != '='; fc_e++)
				 /* 跳过 */ ;
			if (fc_token1[fc_e])		/* 找到 "=" */
			{
				fc_token1[fc_e] = '\0';
				/* 跳过值前的空格 */
				for (fc_token2 = fc_token1 + fc_e + 1; *fc_token2 == ' '; fc_token2++)
					 /* 跳过 */ ;
				/* 找到值的结束 */
				for (fc_a = 0; fc_token2[fc_a] && fc_token2[fc_a] != '&'; fc_a++)
					 /* 跳过 */ ;
				if (fc_token2[fc_a])	/* 找到 "&" => 另一个选项后续 */
				{
					fc_token2[fc_a] = '\0';
					fc_str = fc_token2 + fc_a + 1;
				}
				else
					fc_str = fc_token2 + fc_a;

				fc_conn_keywords[fc_i] = fc_token1;
				fc_conn_values[fc_i] = fc_token2;
				fc_i++;
			}
			else
			{
				/* 无效的选项语法 ... 忽略尾部垃圾 */
				fc_str = fc_token1 + fc_e;
			}
		}
	}

	Assert(fc_i <= fc_connect_params);
	fc_conn_keywords[fc_i] = NULL;	/* 终止符 */

	fc_this->connection = PQconnectdbParams(fc_conn_keywords, fc_conn_values, 0);

	if (fc_host)
		ecpg_free(fc_host);
	if (fc_port)
		ecpg_free(fc_port);
	if (fc_options)
		ecpg_free(fc_options);
	if (fc_dbname)
		ecpg_free(fc_dbname);
	ecpg_free(fc_conn_values);
	ecpg_free(fc_conn_keywords);

	if (PQstatus(fc_this->connection) == CONNECTION_BAD)
	{
		const char *fc_errmsg = PQerrorMessage(fc_this->connection);
		const char *fc_db = fc_realname ? fc_realname : ecpg_gettext("<DEFAULT>");

		/* PQerrorMessage 的结果已经有一个尾随换行 */
		ecpg_log("ECPGconnect: %s", fc_errmsg);

		fc_ecpg_finish(fc_this);
#ifdef ENABLE_THREAD_SAFETY
		pthread_mutex_unlock(&connections_mutex);
#endif

		ecpg_raise(fc_lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, fc_db);
		if (fc_realname)
			ecpg_free(fc_realname);

		return false;
	}

	if (fc_realname)
		ecpg_free(fc_realname);

#ifdef ENABLE_THREAD_SAFETY
	pthread_mutex_unlock(&connections_mutex);
#endif

	fc_this->autocommit = fc_autocommit;

	PQsetNoticeReceiver(fc_this->connection, &fc_ECPGnoticeReceiver, (void *) fc_this);

	return true;
}

bool ECPGdisconnect(int fc_lineno, const char *fc_connection_name)
{
	struct sqlca_t *fc_sqlca = ECPGget_sqlca();
	struct connection *fc_con;

	if (fc_sqlca == NULL)
	{
		ecpg_raise(fc_lineno, ECPG_OUT_OF_MEMORY,
				   ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
		return false;
	}

#ifdef ENABLE_THREAD_SAFETY
	pthread_mutex_lock(&connections_mutex);
#endif

	if (strcmp(fc_connection_name, "ALL") == 0)
	{
		ecpg_init_sqlca(fc_sqlca);
		for (fc_con = all_connections; fc_con;)
		{
			struct connection *fc_f = fc_con;

			fc_con = fc_con->next;
			fc_ecpg_finish(fc_f);
		}
	}
	else
	{
		fc_con = fc_ecpg_get_connection_nr(fc_connection_name);

		if (!ecpg_init(fc_con, fc_connection_name, fc_lineno))
		{
#ifdef ENABLE_THREAD_SAFETY
			pthread_mutex_unlock(&connections_mutex);
#endif
			return false;
		}
		else
			fc_ecpg_finish(fc_con);
	}

#ifdef ENABLE_THREAD_SAFETY
	pthread_mutex_unlock(&connections_mutex);
#endif

	return true;
}

PGconn * ECPGget_PGconn(const char *fc_connection_name)
{
	struct connection *fc_con;

	fc_con = ecpg_get_connection(fc_connection_name);
	if (fc_con == NULL)
		return NULL;

	return fc_con->connection;
}
